/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-06-05     sethnie       the first version
 */

#include "HAL_OV2640.h"

volatile u_int32_t frame_cnt = 0;
volatile u_int32_t addr_cnt = 0;
volatile u_int32_t href_cnt = 0;
static u_int32_t DVP_ROW_cnt = 0;


/* DVP Work Mode */
#define RGB565_MODE 0
/* DVP Work Mode Selection */
#define DVP_Work_Mode RGB565_MODE

u_int32_t JPEG_DVPDMAaddr0 = 0x20005000;
u_int32_t JPEG_DVPDMAaddr1 = 0x20005000 + OV2640_JPEG_WIDTH;

u_int32_t RGB565_DVPDMAaddr0 = 0x2000A000;
u_int32_t RGB565_DVPDMAaddr1 = 0x2000A000 + RGB565_COL_NUM * 2; // each byte(D9-D2) will take 2 bytes of RAM

u_int32_t get_RGB565_DVPDMAaddr0()
{
    return JPEG_DVPDMAaddr0;
}


void DVP_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
/* @fn      DVP_Init
 *
 * @brief   Init DVP
 *
 * @return  none
 */
void DVP_Init(void)
{
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DVP, ENABLE);
    DVP->CR0 &= ~RB_DVP_MSK_DAT_MOD;

    /* VSYNC、HSYNC - High level active */
    DVP->CR0 |= RB_DVP_D10_MOD | RB_DVP_V_POLAR;
    DVP->CR1 &= ~((RB_DVP_ALL_CLR) | RB_DVP_RCV_CLR);
    DVP->ROW_NUM = RGB565_ROW_NUM; // rows
    DVP->COL_NUM = RGB565_COL_NUM; // cols
    DVP->HOFFCNT = (RGB565_COL_NUM - ROI_WIDTH) / 2;
    DVP->VST = (RGB565_ROW_NUM - ROI_HEIGTH) / 2;
    DVP->CAPCNT = ROI_WIDTH;
    DVP->VLINE = ROI_HEIGTH;
    DVP->CR1 |= RB_DVP_CROP;

    DVP->DMA_BUF0 = RGB565_DVPDMAaddr0; // DMA addr0
    DVP->DMA_BUF1 = RGB565_DVPDMAaddr1; // DMA addr1

    /* Set frame capture rate */
    DVP->CR1 &= ~RB_DVP_FCRC;
    DVP->CR1 |= DVP_RATE_100P; // 100%

    // Interrupt Enable
    DVP->IER |= RB_DVP_IE_STP_FRM;
    DVP->IER |= RB_DVP_IE_FIFO_OV;
    DVP->IER |= RB_DVP_IE_FRM_DONE;
    DVP->IER |= RB_DVP_IE_ROW_DONE;
    DVP->IER |= RB_DVP_IE_STR_FRM;

    NVIC_InitTypeDef NVIC_InitStructure = {0};
    NVIC_InitStructure.NVIC_IRQChannel = DVP_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    DVP->CR1 |= RB_DVP_DMA_EN; // enable DMA
    DVP->CR0 |= RB_DVP_ENABLE; // enable DVP
}



/*********************************************************************
 * @fn      DVP_IRQHandler
 *
 * @brief   This function handles DVP exception.
 *
 * @return  none
 */
void DVP_IRQHandler(void)
{

    if (DVP->IFR & RB_DVP_IF_ROW_DONE)
    {
        /* Write 0 clear 0 */
        DVP->IFR &= ~RB_DVP_IF_ROW_DONE; // clear Interrupt

        if (addr_cnt % 2) // buf0 done
        {
            addr_cnt++;
            // data shift
            for (u16 i = 0; i < RGB565_COL_NUM * 2; ++i)
            {
                *(u8 *)(RGB565_DVPDMAaddr0 + i) = (u8)(*(u16 *)(RGB565_DVPDMAaddr0 + i * 2) >> 2);
            }
            // Send DVP data to LCD
            DMA_Cmd(DMA2_Channel5, DISABLE);
            DMA_SetCurrDataCounter(DMA2_Channel5, LCD_W);
            DMA2_Channel5->MADDR = RGB565_DVPDMAaddr0;
            DMA_Cmd(DMA2_Channel5, ENABLE);
        }
        else // buf1 done
        {
            addr_cnt++;
            // data shift
            for (u16 i = 0; i < RGB565_COL_NUM * 2; ++i)
            {
                *(u8 *)(RGB565_DVPDMAaddr1 + i) = (u8)(*(u16 *)(RGB565_DVPDMAaddr1 + i * 2) >> 2);
            }
            // Send DVP data to LCD
            DMA_Cmd(DMA2_Channel5, DISABLE);
            DMA_SetCurrDataCounter(DMA2_Channel5, LCD_W);
            DMA2_Channel5->MADDR = RGB565_DVPDMAaddr1;
            DMA_Cmd(DMA2_Channel5, ENABLE);
        }
        href_cnt++;
    }

    if (DVP->IFR & RB_DVP_IF_FRM_DONE)
    {
        DVP->IFR &= ~RB_DVP_IF_FRM_DONE; // clear Interrupt
        addr_cnt = 0;
        href_cnt = 0;
    }

    if (DVP->IFR & RB_DVP_IF_STR_FRM)
    {
        DVP->IFR &= ~RB_DVP_IF_STR_FRM;
    }

    if (DVP->IFR & RB_DVP_IF_STP_FRM)
    {
        DVP->IFR &= ~RB_DVP_IF_STP_FRM;
        frame_cnt++;
    }

    if (DVP->IFR & RB_DVP_IF_FIFO_OV)
    {
        DVP->IFR &= ~RB_DVP_IF_FIFO_OV;
        rt_kprintf("DVP FIFO OV\r\n");
    }
}

rt_bool_t ov2640_init()
{
    rt_err_t ret = RT_EOK;
    /* Camera init */
    while (OV2640_Init())
    {
        rt_kprintf("Camera Err\r\n");
        rt_thread_mdelay(500);
    }
    rt_kprintf("Camera Success\r\n");
    rt_thread_mdelay(500);

    /* Camera mode */
    RGB565_Mode_Init();
    rt_kprintf("RGB565_MODE\r\n");
    rt_thread_mdelay(500);
    return ret;
}

/* 导出到 msh 命令列表中 */
//INIT_APP_EXPORT(ov2640_init);
